#pragma once

// Thanks hatf0
// https://github.com/hatf0/TraceEnabler

// ripped out structs from Tork

typedef unsigned int U32;
typedef float F32;
typedef signed int S32;
typedef const char* StringTableEntry;

typedef const char* (*StringCallback)(DWORD* obj, int argc, const char* argv[]);
typedef int(*IntCallback)(DWORD* obj, int argc, const char* argv[]);
typedef float(*FloatCallback)(DWORD* obj, int argc, const char* argv[]);
typedef void(*VoidCallback)(DWORD* obj, int argc, const char* argv[]);
typedef bool(*BoolCallback)(DWORD* obj, int argc, const char* argv[]);


struct Namespace
{
	const char* mName;
	const char* mPackage;

	Namespace* mParent;
	Namespace* mNext;
	void* mClassRep;
	U32 mRefCountToParent;
	struct Entry
	{
		enum
		{
			GroupMarker = -3,
			OverloadMarker = -2,
			InvalidFunctionType = -1,
			ScriptFunctionType,
			StringCallbackType,
			IntCallbackType,
			FloatCallbackType,
			VoidCallbackType,
			BoolCallbackType
		};

		Namespace* mNamespace;
		//char _padding1[4];
		Entry* mNext;
		const char* mFunctionName;
		S32 mType;
		S32 mMinArgs;
		S32 mMaxArgs;
		const char* mUsage;
		const char* mPackage;
		void* mCode; // CodeBlock *mCode;
		U32 mFunctionOffset;
		union {
			StringCallback mStringCallbackFunc;
			IntCallback mIntCallbackFunc;
			VoidCallback mVoidCallbackFunc;
			FloatCallback mFloatCallbackFunc;
			BoolCallback mBoolCallbackFunc;
			const char* mGroupName;
		} cb;
	};
	Entry* mEntryList;
	Entry** mHashTable;
	U32 mHashSize;
	U32 mHashSequence;  ///< @note The hash sequence is used by the autodoc console facility
						///        as a means of testing reference state.
	char* lastUsage;
};

//static Namespace* mGlobalNamespace;

struct CodeBlock
{
	const char* name;
	int unk_1;
	void* globalStrings;
	void* functionStrings;
	void* globalFloats;
	void* functionFloats;
	int codeSize;
	int unk_2;
	char* code;
	int refCount;
	int lineBreakPairCount;
	int* lineBreakPairs;
	int breakListSize;
	int* breakList;
	CodeBlock* nextFile;
	const char* mRoot;
};

struct Dictionary
{
	struct Entry {
		enum
		{
			TypeInternalInt = -3,
			TypeInternalFloat = -2,
			TypeInternalString = -1,
		};

		const char* name;
		Entry* next;
		S32 type;
		char* sval;
		U32 ival; // doubles as strlen
		F32 fval;
		U32 bufferLen;
		void* dataPtr;
	};

	struct HashTableData
	{
		int size;
		int count;
		Entry** data;
		void* owner;
	};

	HashTableData table;
	const char* scopeName;
	const char* scopeNamespace;
	CodeBlock* block;
	int ip;
};

struct ExprEvalState
{
	void* thisObject;
	Dictionary::Entry* currentVariable;
	bool traceOn;
	Dictionary globalVars;
	Vector<Dictionary> stack;
};

//static ExprEvalState* gEvalState;

struct DataChunker
{
	struct DataBlock
	{
		DataBlock* next;
		char* data;
		int curIndex;
	};

	DataBlock* curBlock;
	signed int chunkSize;
};

struct StringStack
{
	char* mBuffer;
	int mBufferSize;
	const char* mArgv[20];
	int mFrameOffsets[1024];
	int mStartOffsets[1024];
	int mNumFrames;
	int mArgc;
	int mStart;
	int mLen;
	int mStartStackSize;
	int mFunctionOffset;
	int mArgBufferSize;
	char* mArgBuffer;
};

struct StringTable {
	struct Node {
		char* val;
		Node* next;
	};

	Node** buckets;
	U32 numBuckets;
	U32 itemCount;
	DataChunker memPool;
};



//New types not from TraceEnabler

struct Notify
{
	enum Type
	{
		ClearNotify,   ///< Notified when the object is cleared.
		DeleteNotify,  ///< Notified when the object is deleted.
		ObjectRef,     ///< Cleverness to allow tracking of references.
		Invalid        ///< Mark this notification as unused (used in freeNotify).
	} type;
	void* ptr;        ///< Data (typically referencing or interested object).
	Notify* next;     ///< Next notification in the linked list.
};



//Implementation of a Point3F class and Torque's MatrixF
//Borrowed from Valcle's VehicleWalking and touched up a little

class Point3F
{
public:
	float pX, pY, pZ;

	Point3F(float x, float y, float z)
	{
		pX = x;
		pY = y;
		pZ = z;
	}

	Point3F() {};

	Point3F operator+(Point3F& other)
	{
		return Point3F(pX + other[0],
					   pY + other[1],
					   pZ + other[2]);
	}

	Point3F operator+(Point3F&& other)
	{
		return operator+(other);
	}

	Point3F operator-(Point3F& other)
	{
		return Point3F(pX - other[0],
					   pY - other[1],
					   pZ - other[2]);
	}

	Point3F operator-(Point3F&& other)
	{
		return operator-(other);
	}

	Point3F operator*(float fScalar)
	{
		return Point3F(pX * fScalar,
					   pY * fScalar,
					   pZ * fScalar);
	}

	Point3F operator/(float fScalar)
	{
		return Point3F(pX / fScalar,
					   pY / fScalar,
					   pZ / fScalar);
	}

	Point3F ptNorm()
	{
		float fLn = fLen();

		return Point3F(pX / fLn,
					   pY / fLn,
					   pZ / fLn);
	}

	float& operator[](unsigned int i)
	{
		if(i == 0)
			return pX;

		if(i == 1)
			return pY;

		return pZ;
	}

	float fLen()
	{
		return sqrtf((pX * pX) +
					 (pY * pY) +
					 (pZ * pZ));
	}
};

class MatrixF
{
public:
	float fM[16];

	Point3F ptGetPosition()
	{
		return Point3F(fM[3], fM[7], fM[11]);
	}

	Point3F ptGetForwardVector()
	{
		return Point3F(fM[1], fM[5], fM[9]);
	}

	void GetColumn(int iCol, Point3F* ptRet)
	{
		ptRet[0][0] = fM[iCol];
		ptRet[0][1] = fM[iCol + 4];
		ptRet[0][2] = fM[iCol + 8];
	}

	void SetColumn(int iCol, Point3F* ptSet)
	{
		fM[iCol] = ptSet[0][0];
		fM[iCol + 4] = ptSet[0][1];
		fM[iCol + 8] = ptSet[0][2];
	}

	float Determinate()
	{
		return fM[0] * (fM[5] * fM[10] - fM[6] * fM[9]) +
			fM[4] * (fM[2] * fM[9] - fM[1] * fM[10]) +
			fM[8] * (fM[1] * fM[6] - fM[2] * fM[5]);
	}

	void Invert()
	{
		float inv[16], det;
		int i;

		inv[0] = fM[5] * fM[10] * fM[15] -
			fM[5] * fM[11] * fM[14] -
			fM[9] * fM[6] * fM[15] +
			fM[9] * fM[7] * fM[14] +
			fM[13] * fM[6] * fM[11] -
			fM[13] * fM[7] * fM[10];

		inv[4] = -fM[4] * fM[10] * fM[15] +
			fM[4] * fM[11] * fM[14] +
			fM[8] * fM[6] * fM[15] -
			fM[8] * fM[7] * fM[14] -
			fM[12] * fM[6] * fM[11] +
			fM[12] * fM[7] * fM[10];

		inv[8] = fM[4] * fM[9] * fM[15] -
			fM[4] * fM[11] * fM[13] -
			fM[8] * fM[5] * fM[15] +
			fM[8] * fM[7] * fM[13] +
			fM[12] * fM[5] * fM[11] -
			fM[12] * fM[7] * fM[9];

		inv[12] = -fM[4] * fM[9] * fM[14] +
			fM[4] * fM[10] * fM[13] +
			fM[8] * fM[5] * fM[14] -
			fM[8] * fM[6] * fM[13] -
			fM[12] * fM[5] * fM[10] +
			fM[12] * fM[6] * fM[9];

		inv[1] = -fM[1] * fM[10] * fM[15] +
			fM[1] * fM[11] * fM[14] +
			fM[9] * fM[2] * fM[15] -
			fM[9] * fM[3] * fM[14] -
			fM[13] * fM[2] * fM[11] +
			fM[13] * fM[3] * fM[10];

		inv[5] = fM[0] * fM[10] * fM[15] -
			fM[0] * fM[11] * fM[14] -
			fM[8] * fM[2] * fM[15] +
			fM[8] * fM[3] * fM[14] +
			fM[12] * fM[2] * fM[11] -
			fM[12] * fM[3] * fM[10];

		inv[9] = -fM[0] * fM[9] * fM[15] +
			fM[0] * fM[11] * fM[13] +
			fM[8] * fM[1] * fM[15] -
			fM[8] * fM[3] * fM[13] -
			fM[12] * fM[1] * fM[11] +
			fM[12] * fM[3] * fM[9];

		inv[13] = fM[0] * fM[9] * fM[14] -
			fM[0] * fM[10] * fM[13] -
			fM[8] * fM[1] * fM[14] +
			fM[8] * fM[2] * fM[13] +
			fM[12] * fM[1] * fM[10] -
			fM[12] * fM[2] * fM[9];

		inv[2] = fM[1] * fM[6] * fM[15] -
			fM[1] * fM[7] * fM[14] -
			fM[5] * fM[2] * fM[15] +
			fM[5] * fM[3] * fM[14] +
			fM[13] * fM[2] * fM[7] -
			fM[13] * fM[3] * fM[6];

		inv[6] = -fM[0] * fM[6] * fM[15] +
			fM[0] * fM[7] * fM[14] +
			fM[4] * fM[2] * fM[15] -
			fM[4] * fM[3] * fM[14] -
			fM[12] * fM[2] * fM[7] +
			fM[12] * fM[3] * fM[6];

		inv[10] = fM[0] * fM[5] * fM[15] -
			fM[0] * fM[7] * fM[13] -
			fM[4] * fM[1] * fM[15] +
			fM[4] * fM[3] * fM[13] +
			fM[12] * fM[1] * fM[7] -
			fM[12] * fM[3] * fM[5];

		inv[14] = -fM[0] * fM[5] * fM[14] +
			fM[0] * fM[6] * fM[13] +
			fM[4] * fM[1] * fM[14] -
			fM[4] * fM[2] * fM[13] -
			fM[12] * fM[1] * fM[6] +
			fM[12] * fM[2] * fM[5];

		inv[3] = -fM[1] * fM[6] * fM[11] +
			fM[1] * fM[7] * fM[10] +
			fM[5] * fM[2] * fM[11] -
			fM[5] * fM[3] * fM[10] -
			fM[9] * fM[2] * fM[7] +
			fM[9] * fM[3] * fM[6];

		inv[7] = fM[0] * fM[6] * fM[11] -
			fM[0] * fM[7] * fM[10] -
			fM[4] * fM[2] * fM[11] +
			fM[4] * fM[3] * fM[10] +
			fM[8] * fM[2] * fM[7] -
			fM[8] * fM[3] * fM[6];

		inv[11] = -fM[0] * fM[5] * fM[11] +
			fM[0] * fM[7] * fM[9] +
			fM[4] * fM[1] * fM[11] -
			fM[4] * fM[3] * fM[9] -
			fM[8] * fM[1] * fM[7] +
			fM[8] * fM[3] * fM[5];

		inv[15] = fM[0] * fM[5] * fM[10] -
			fM[0] * fM[6] * fM[9] -
			fM[4] * fM[1] * fM[10] +
			fM[4] * fM[2] * fM[9] +
			fM[8] * fM[1] * fM[6] -
			fM[8] * fM[2] * fM[5];

		det = fM[0] * inv[0] + fM[1] * inv[4] + fM[2] * inv[8] + fM[3] * inv[12];
		det = 1.0f / det;

		for (i = 0; i < 16; i++)
			fM[i] = inv[i] * det;
	}

	void MulVector(Point3F& ptVec, Point3F* ptOut)
	{
		ptOut[0][0] = fM[0] * ptVec[0] + fM[1] * ptVec[1] + fM[2]  * ptVec[2];
		ptOut[0][1] = fM[4] * ptVec[0] + fM[5] * ptVec[1] + fM[6]  * ptVec[2];
		ptOut[0][2] = fM[8] * ptVec[0] + fM[9] * ptVec[1] + fM[10] * ptVec[2];
	}

	void MulPoint(Point3F& ptVec, Point3F* ptOut)
	{
		ptOut[0][0] = fM[0] * ptVec[0] + fM[1] * ptVec[1] + fM[2]  * ptVec[2] + fM[3];
		ptOut[0][1] = fM[4] * ptVec[0] + fM[5] * ptVec[1] + fM[6]  * ptVec[2] + fM[7];
		ptOut[0][2] = fM[8] * ptVec[0] + fM[9] * ptVec[1] + fM[10] * ptVec[2] + fM[11];
	}
};
